import { _decorator, Component, Node, Vec3, EventTouch, Enum, error, UITransform, Vec2, UIOpacity, CCFloat } from 'cc';
import { globalObserver } from '../observer/GlobalObserver';
const { ccclass, property } = _decorator;

/**
 * direction type
 */
export enum DirectionType{
    FOUR,
    EIGHT,
    ALL,
}

/**
 * speed type
 */
export enum SpeedType{
    START,
    STOP,
    NORMAL,
    FAST,
}

/**
 * joystick type
 */
export enum JoystickType{
    FIXED,
    FOLLOW,
}

/**
 * display type
 */
export enum JoystickDisplayType{
    ALWAYS_DISPLAY,
    TOUCH_DISPLAY,
}

/**
 * joystick data type
 */
export interface JoystickDataType{
    /** speed type */
    type: SpeedType;

    /** move vector */
    vector: Vec3;

    /** angle */
    angle: number;
}

export type ControllerFunc = (event: EventTouch, data:JoystickDataType) => void;

@ccclass('JoystickController')
export class JoystickController extends Component {

    // get joystick node
    @property(Node)
    public joystick: Node | null = null;

    // get joystick background node
    @property(Node)
    public joystick_back: Node | null = null;

    // joystick type
    @property({type: Enum(JoystickType)})
    public joystickType = JoystickType.FIXED;

    // direction type
    @property({type: Enum(DirectionType)})
    public directionType = DirectionType.ALL;

    // display type
    @property({type: Enum(JoystickDisplayType)})
    public displayType = JoystickDisplayType.ALWAYS_DISPLAY;

    // radius
    @property(CCFloat)
    public radius: number = -1;

    // joystick initial position
    public stickLoc: Vec3 = new Vec3(0, 0, 0);
    // touch position
    public touchLoc: Vec2 | null = null;
    // joystick move direction
    private joyMoveDir: Vec3 | null = new Vec3(0, 0, 0);


    onLoad() {
        if (!this.joystick){
            error('Joystick is empty');
            return;
        }

        if (!this.joystick_back){
            error('Joystick backgroud is empty');
            return;
        }
        
        // set joystick background radius
        if (this.radius == -1)
            this.radius = (this.joystick_back.getComponent(UITransform)!.width/2)*this.node.getScale().x;

        // hide Ui
        const uiOpacity = this.node.getComponent(UIOpacity);
        if (this.joystickType === JoystickType.FOLLOW && uiOpacity){
            uiOpacity.opacity = 0;
        } 

        this._initTouchEvent();
    }


    private _initTouchEvent() {
        this.node.on(Node.EventType.TOUCH_START, this._touchStartEvent, this);
        this.node.on(Node.EventType.TOUCH_MOVE, this._touchMoveEvent, this);
        this.node.on(Node.EventType.TOUCH_END, this._touchEndEvent, this);
        this.node.on(Node.EventType.TOUCH_CANCEL, this._touchEndEvent, this);
    }

    /**
     * touch start callback
     * @param event
     */
    private _touchStartEvent(event: EventTouch){
        if (!this.joystick || !this.joystick_back) return;

        /**
         * //open joystick move
         * var player: Player = GameWorld.instance.gameMap.myPlayer;
         * player.navAgent.navPath.stop();      // stop auto move
         * 
         * // if player is tracking target, stop first
         * if (player.trackLogic target != null) player.trackLogic.stopTrack();
         * 
         * GameController.instance.controlMode = ControlMode.joystick;
         * GameWorld.instance.gameMap.myPlayer.controlMode = gameController.instance.controlMode;
         */

        // touch position
        const location = event.getUILocation();
        const touchPos = new Vec3(location.x, location.y, 0);

        if (this.joystickType === JoystickType.FIXED) {
            this.stickLoc = this.joystick_back.getWorldPosition();

            // vector compare to midpoint
            const moveVec = touchPos.subtract(this.joystick_back.getWorldPosition());
            const distance = moveVec.length();

            // joystick follow point
            if (this.radius > distance){
                this.joystick.setPosition(moveVec);
                this.joyMoveDir = moveVec.normalize();
            }

        } else if (this.joystickType === JoystickType.FOLLOW) {
            this.node.getComponent(UIOpacity)!.opacity = 255;
            this.stickLoc = touchPos;
            this.touchLoc = event.getUILocation();

            // change joystick position
            this.joystick.setWorldPosition(touchPos);
            this.joystick_back.setPosition(Vec3.ZERO);
        }

    }

    /**
     * touch move callback
     * @param event 
     */
    private _touchMoveEvent(event: EventTouch){
        if (!this.joystick || !this.joystick_back) return;

        // if touch position is same with initial position, avoid move
        if (this.joystickType === JoystickType.FOLLOW && this.touchLoc === event.getUILocation()){
            return false;
        }

        // get touch position with origin
        const location = event.getUILocation();
        const touchPos = new Vec3(location.x, location.y, 0);

        // vector compare to midpoint
        const moveVec = touchPos.subtract(this.stickLoc);
        const distance = moveVec.length();

        let speedType = SpeedType.NORMAL;
        if (this.radius > distance){
            this.joystick.setPosition(moveVec);
            speedType = SpeedType.NORMAL;
        } else {
            // keep joystick in the ring and keep update location
            this.joystick.setPosition(moveVec.normalize().multiplyScalar(this.radius));
            speedType = SpeedType.FAST;
        }
        // get vec angle
        this.joyMoveDir = moveVec.normalize();
    }

    /**
     * touch end callback
     * @param event 
     */
    private _touchEndEvent(event: TouchEvent){
        if (!this.joystick || !this.joystick_back) return;

        this.joyMoveDir.x = 0;
        this.joyMoveDir.y = 0;

        setTimeout(() => {
            /**
             * // close joystick move
             * GameController.instance.controlMode = ControlMode.touch;
             * GameWorld.instance.gameMap.myPlayer.controlMode = GameController.instance.controlMode;
             */
        }, 100);

        this.joystick.setPosition(new Vec3(0, 0, 0));
        if(this.joystickType === JoystickType.FOLLOW){
            this.node.getComponent(UIOpacity)!.opacity = 0;
        }
    }



    protected update(dt): void {
        if (this.joyMoveDir.x != 0 && this.joyMoveDir.y != 0) {
            globalObserver.emit('control-move', this.joyMoveDir);
        }
    }
}


